package com.innerchic.sponsor;

import android.os.Looper;

import com.innerchic.sponsor.builder.DefaultSponsorBuilder;
import com.innerchic.sponsor.builder.SponsorBuilder;
import com.innerchic.sponsor.builder.SubMethodFind;
import com.innerchic.sponsor.builder.SubObject;
import com.innerchic.sponsor.other.ObjectTag;
import com.innerchic.sponsor.other.Subject;
import com.innerchic.sponsor.poster.Poster;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 作者:贾恒飞
 * 应用于发起者跨进程响应事件驱动
 * 可用作线程调度使用
 * 当前对象单例存在
 */
public class Sponsor {
    private static final String TAG = "Sponsor";

    private Sponsor() {
        this(new DefaultSponsorBuilder());
    }

    /**
     * default sponsor object
     */
    private static Sponsor defaultSponsorInstance;

    /**
     * Get default object
     *
     * @return SPONSOR.CLASS Object
     */
    public static Sponsor getDefault() {
        Sponsor mInstance = defaultSponsorInstance;
        if (mInstance == null) {
            synchronized (Sponsor.class) {
                mInstance = defaultSponsorInstance = new Sponsor();
            }
        }
        return mInstance;
    }

    /**
     * sponsor builder
     */
    private final SponsorBuilder sponsorBuilder;

    /**
     * 订阅对象筛选集
     */
    private final SubMethodFind subMethodFind;

    /**
     * 当前注册的类集合
     */
    private final Map<String, Map<String, Object>> registerObject;

    /**
     * 推送的方法
     */
    private final Map<String, List<SubObject>> postMethodMap;

    /**
     * 主进程
     */
    private final Poster mainPoster;

    public Sponsor(SponsorBuilder sponsorBuilder) {
        this.sponsorBuilder = sponsorBuilder;
        subMethodFind = new SubMethodFind();
        registerObject = new HashMap<>();
        postMethodMap = new ConcurrentHashMap<>();
        mainPoster = sponsorBuilder.getMainPoster();
    }

    /**
     * 推送
     *
     * @param objects 要推送的数据
     */
    public void post(Object... objects) {
        if (objects.length == 0) return;
        StringBuilder stringBuilder = new StringBuilder();
        for (Object object : objects) {
            if (object == null) continue;
            stringBuilder.append(object.getClass().getName()).append("&");
        }
        String toKey = stringBuilder.toString();
        List<SubObject> subObjects = postMethodMap.get(toKey);
        if (subObjects == null) return;
        for (SubObject subObject : subObjects) {
            if (subObject == null) continue;
            execute(subObject, objects);
        }
    }

    /**
     * 执行
     */
    private void execute(SubObject subObject, Object... objects) {
        String fromClassName = subObject.getFromClassName();
        Map<String, Object> stringObjectMap = registerObject.get(fromClassName);
        if (stringObjectMap == null) return;
        //判断线程
        switch (subObject.getSubject().
                thread()) {
            case Subject
                    .THREAD_MAIN:
                executeWithMain(subObject, stringObjectMap, objects);
                break;
            case Subject
                    .THREAD_MAIN_IF_NOT:
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    executeWithNowThread(subObject, stringObjectMap, objects);
                } else
                    executeWithMain(subObject, stringObjectMap, objects);
                break;
            case Subject
                    .THREAD_NEW:
                new Thread(() -> executeWithNowThread(subObject, stringObjectMap, objects)).start();
                break;
            case Subject
                    .THREAD_POOL:
                sponsorBuilder.getThreadPool().execute(() -> executeWithNowThread(subObject, stringObjectMap, objects));
                break;
            case Subject
                    .THREAD_AUTO:
            default:
                executeWithNowThread(subObject, stringObjectMap, objects);
        }
    }

    /**
     * 执行在主线程
     */
    private void executeWithMain(SubObject subObject, Map<String, Object> stringObjectMap, Object... objects) {
        Set<String> strings = stringObjectMap.keySet();
        for (String string : strings) {
            Object o = stringObjectMap.get(string);
            if (o == null) continue;
            subObject.invoke(mainPoster, o, objects);
        }
    }

    /**
     * 执行在当前线程
     */
    private void executeWithNowThread(SubObject subObject, Map<String, Object> stringObjectMap, Object... objects) {
        Set<String> strings = stringObjectMap.keySet();
        for (String string : strings) {
            Object o = stringObjectMap.get(string);
            if (o == null) continue;
            subObject.invoke(null, o, objects);
        }
    }

    /**
     * 注册
     *
     * @param object 注册对象
     */
    public void register(Object object) {
        if (object == null) return;
        List<SubObject> subMethod = subMethodFind.findSubMethod(object);
        synchronized (this) {
            for (SubObject subObject : subMethod) {
                subscribe(object, subObject);
            }
        }
    }

    /**
     * 将当前方法订阅到对应的监听下
     *
     * @param object    对象
     * @param subObject 订阅方法
     */
    private void subscribe(Object object, SubObject subObject) {
        if (object == null || subObject == null) return;
        String className = object.getClass().getName();
        if (!registerObject.containsKey(className)) {
            registerObject.put(object.getClass().getName(), new HashMap<>());
        }
        Map<String, Object> stringObjectMap = registerObject.get(className);
        String subscribeKey = null;
        if (object instanceof ObjectTag) subscribeKey = ((ObjectTag) object).uniqueness();
        assert stringObjectMap != null;
        stringObjectMap.put(subscribeKey, object);
        String classNames = subObject.getClassNames();
        if (!postMethodMap.containsKey(classNames)) {
            postMethodMap.put(classNames, new ArrayList<>());
        }
        List<SubObject> subObjects = postMethodMap.get(classNames);
        assert subObjects != null;
        subObjects.add(subObject);
        //优先级排序
        Collections.sort(subObjects);
    }

    /**
     * 解注册
     */
    public void unregister(Object object) {
        if (object == null) return;
        List<SubObject> subMethod = subMethodFind.findSubMethod(object);
        synchronized (this) {
            for (SubObject subObject : subMethod) {
                String classNames = subObject.getClassNames();
                postMethodMap.remove(classNames);
                String className = object.getClass().getName();
                Map<String, Object> stringObjectMap = registerObject.get(className);
                String subscribeKey = null;
                if (object instanceof ObjectTag) subscribeKey = ((ObjectTag) object).uniqueness();
                assert stringObjectMap != null;
                stringObjectMap.remove(subscribeKey);
            }
        }
    }

}
